package com.example.jjj;

import java.util.ArrayList;
import java.util.List;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.LinearInterpolator;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;

@SuppressLint("NewApi")
public class D3ListView extends FrameLayout {
    private static final float DEF_NUM = 5;

	private float aNum = DEF_NUM, count = 0;
	private final float num = 5;
	private int mHeight, mWidth;

	private float mScrollY = 0;
	private float mMaxScrollY = 0, mMinScrollY = 0;

	private boolean interceptTouchEvent = false;
	private boolean disallowIntercept = false;
	private boolean horibleScrollEvent = false;

	private GestureDetector detector;

	/** ����Ħ��������ɵļ��ٶ�,ʹ����ͣ���������� */
	private static final float ACC = 10f / 1000;

	private View mScrollChild;
	private int mScrollPosition = -1;

	public D3ListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public D3ListView(Context context) {
		super(context);
		init();
	}

	public void init() {
		// setClipToPadding(false);
		detector = new GestureDetector(getContext(), onGestureListener);
	}

	public float getVisableNum() {
		return aNum;
	}

	public void setVisableNum(float num) {
		this.aNum = num;
	}

	public void setAdapter(BaseAdapter adapter) {
		LayoutParams p = new LayoutParams(-1, -2);
		count = adapter.getCount();
		reBoundByCountChange();

		for (int i = 0; i < count; i++) {
			View view = adapter.getView(i, null, this);
			addView(view, p);
		}
		postAdjustChild();
	}

	private void postAdjustChild() {
		getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

			@Override
			public void onGlobalLayout() {
				getViewTreeObserver().removeOnGlobalLayoutListener(this);
				adjustChild();
			}
		});
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		mHeight = getMeasuredHeight();
		mWidth = getMeasuredWidth();
		for (int i = 0; i < getChildCount(); i++) {
			View child = getChildAt(i);
			child.setPivotY(0);
			child.setPivotX(child.getMeasuredWidth() / 2f);
		}

		reBoundByCountChange();
	}

	/**
	 * @���� ��count�ı�ʱ����ĳЩ����
	 * 
	 * @author px / 2016-3-5
	 */
	private void reBoundByCountChange() {
		if (count < num) {
			mMaxScrollY = 0.3f * num;
			mMinScrollY = -count / (count + 1);
		} else {
			mMaxScrollY = count - num + 0.1f * num;
			mMinScrollY = -0.4f * num;
		}
	}

	private void adjustChild() {
		for (int i = 0; i < getChildCount(); i++) {
			View child = getChildAt(i);
			// λ����Ϣת���ɼ򵥵ĳ�0~1�任�ĸ�����,��ʾ���ϵ��·����Yֵ.
			float value = convertVariable(i);

			// �ѱ���Ӧ�õ�yֵ
			float y = layoutY(value);
			// �����ʽ��ʾ��С���ϵ��°�0.8��1�ֲ�
			float scale = 0.8f + y / mHeight * 0.2f;

			// topԽС,���Լ�ԽС
			child.setScaleX(scale);
			child.setScaleY(scale);
			child.setY(y);
		}
	}

	private float lastX, lastY;

	// public boolean onInterceptTouchEvent(MotionEvent e) {
	public void handleInterceptTouchEvent(MotionEvent e) {
		switch (e.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
			lastX = e.getX();
			lastY = e.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			// �ӿؼ�Ҫ�¼�,�����
			if (disallowIntercept) {
				break;
			}
			if (!interceptTouchEvent && Math.abs(e.getX() - lastX) > 50) {
				horibleScrollEvent = true;
				break;
			}
			if (Math.abs(e.getY() - lastY) > 50) {
				interceptTouchEvent = true;
			}
			break;
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			break;
		default:
			break;
		}
	}

	private void handleHoribleScroll(MotionEvent e) {
		detector.onTouchEvent(e);
		switch (e.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
			// ����������View
			if (getChildCount() == 1) {
				mScrollPosition = 0;
				mScrollChild = getChildAt(0);
				break;
			} else if (getChildCount() == 0) {
				mScrollPosition = -1;
				mScrollChild = null;
				break;
			}
			for (int i = 0; i < getChildCount(); i++) {
				View child = getChildAt(i);
				if (child.getY() >= e.getY()) {
					mScrollPosition = i - 1;
					mScrollChild = getChildAt(mScrollPosition);
					break;
				}
			}
			if (mScrollChild == null) {
				mScrollPosition = getChildCount() - 1;
				mScrollChild = getChildAt(mScrollPosition);
			}
			break;
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			if (horibleScrollEvent && mScrollChild != null) {
				// ���򵯻ػ��߷ɳ�
				bounceOrFlyOut(mScrollChild);
			}
			break;
		default:
			break;
		}
	}

	public boolean hanScrollTouchEvent(MotionEvent e) {
		detector.onTouchEvent(e);
		switch (e.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			if (mScrollY > mMaxScrollY || mScrollY < mMinScrollY) {
				bounce();
			}
			break;
		default:
			break;
		}
		return false;
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		return false;
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent e) {
		if ((e.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
			if (animator != null && animator.isRunning()) {
				animator.cancel();
			}
		}
		if (horibleScrollEvent) {
			// flag = super.dispatchTouchEvent(e);
			handleHoribleScroll(e);
		} else if (interceptTouchEvent) {
			hanScrollTouchEvent(e);
		} else {

			// ͬʱ����
			hanScrollTouchEvent(e);
			handleHoribleScroll(e);
			super.dispatchTouchEvent(e);

			// ���������߼�,�����´��Ƿ�����
			handleInterceptTouchEvent(e);
		}

		switch (e.getAction() & MotionEvent.ACTION_MASK) {

		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			interceptTouchEvent = disallowIntercept = horibleScrollEvent = false;
			mScrollChild = null;
			break;
		default:
			if (horibleScrollEvent || interceptTouchEvent) {
				e.setAction(MotionEvent.ACTION_CANCEL);
				super.dispatchTouchEvent(e);
			}
			break;
		}

		return true;
	}

	@Override
	public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
		super.requestDisallowInterceptTouchEvent(disallowIntercept);
		this.disallowIntercept = disallowIntercept;
	}

	/**
	 * @���� ����
	 * 
	 * @author px / 2016-3-4
	 */
	private void bounce() {
		float targetY;
		if (mScrollY < mMinScrollY) {
			targetY = mMinScrollY;
		} else if (mScrollY > mMaxScrollY) {
			targetY = mMaxScrollY;
		} else {
			return;
		}
		if (animator != null && animator.isRunning()) {
			animator.cancel();
		}
		animator = ValueAnimator.ofFloat(mScrollY, targetY);
		animator.addUpdateListener(bounceListener);
		animator.setDuration(250);
		animator.start();
	}

	/**
	 * @���� ���򷴵���ɳ�ȥ
	 * 
	 * @author px / 2016-3-5
	 */
	private void bounceOrFlyOut(final View child) {
		final float fromX = child.getX();
		// child������λ��
		float centerX = fromX + child.getWidth() / 2f;
		final boolean delete;
		// �ж�Ŀ��λ��,�ǵ��ػ��Ƿɳ�
		final float toX;
		if (centerX < 0) {
			toX = -mWidth;
			delete = true;
		} else if (centerX > mWidth) {
			toX = mWidth;
			delete = true;
		} else {
			toX = 0;
			delete = false;
		}

		final List<View> list;
		final List<Float> fromYs = new ArrayList<Float>();
		final List<Float> toYs = new ArrayList<Float>();
		int f, t;
		if (delete) {
			if (onDeleteChildListener != null) {
				onDeleteChildListener.onDelete(mScrollPosition);
			}
			count--;
			reBoundByCountChange();
			// �ж���Ҫƽ������item
			if (count < num) {
				// item��������,��Ҫ����numʱ,��ȫ��View����
				// item̫��, ͣ����λ�ñ�Ȼ����С����.
				list = calculateTranslatePosition(0, (int) count + 1, mScrollY = mMinScrollY, toYs);
			} else if (mScrollY < count - num + 1) {// �·���������һ��Viewʱ,����ʹ�·���item��������
				f = mScrollPosition + 1;
				t = (int) count + 1;
				// �����������ʵ��scrollֵ����
				list = calculateTranslatePosition(f, t, mScrollY, toYs);
			} else if (mScrollY > 0) {
				// �·�View����ʱʹ���Ϸ���View����,
				// ʵ��Ӧ�õ���mScrollYֵ�任��mScrollY-1
				f = (int) (mScrollY - 1);
				t = mScrollPosition;
				list = calculateTranslatePosition(f, t, --mScrollY, toYs);
			} else {

				throw new RuntimeException("trueNum=" + num + ",   mScrollY=" + ",   mScrollPosition="
						+ mScrollPosition);
			}
			for (int i = 0; i < list.size(); i++) {
				fromYs.add(list.get(i).getY());
			}

		} else {
			list = null;
		}
		if (animator != null && animator.isRunning()) {
			animator.cancel();
		}
		animator = ValueAnimator.ofFloat(0, 1);
		// ���������500���ڷ������
		long during = (long) (Math.abs(toX - fromX) / mWidth * 1000);
		animator.setDuration(500);
		animator.addListener(new SimpleAnimatorListener() {

			public void onAnimationCancel(Animator animation) {
				child.setX(toX);
				if (delete) {
					removeView(child);
					postAdjustChild();
				}
			}

			@Override
			public void onAnimationEnd(Animator animation) {
				if (delete) {
					removeView(child);
					postAdjustChild();
				}
			}
		});
		animator.addUpdateListener(new AnimatorUpdateListener() {

			@Override
			public void onAnimationUpdate(ValueAnimator animation) {

				Float v = (Float) animation.getAnimatedValue();
				child.setX(v * (toX - fromX) + fromX);

				if (delete) {
					for (int i = 0; i < list.size(); i++) {

						View view = list.get(i);
						float y = v * (toYs.get(i) - fromYs.get(i)) + fromYs.get(i);
						view.setY(y);
						// �����ʽ��ʾ��С���ϵ��°�0.8��1�ֲ�
						float scale = 0.8f + y / mHeight * 0.2f;

						// topԽС,���Լ�ԽС
						view.setScaleX(scale);
						view.setScaleY(scale);
					}
				}
			}
		});
		animator.start();
	}

	private List<View> calculateTranslatePosition(int from, int to, float scrollY, List<Float> list) {
		List<View> views = new ArrayList<View>();
		for (int i = from; i < to; i++) {
			float j;
			if (i == mScrollPosition) {
				continue;
			} else if (i < mScrollPosition) {
				j = i;
			} else {
				j = i - 1;
			}
			View view = getChildAt(i);

			// λ����Ϣת���ɼ򵥵ĳ�0~1�任�ĸ�����,��ʾ���ϵ��·����Yֵ.
			float value = convertVariable(j);
			float y = layoutY(value);

			if (y < 0) {
				y = 0;
			}
			views.add(view);
			list.add(y);
		}
		return views;
	}

	AnimatorUpdateListener bounceListener = new AnimatorUpdateListener() {

		@Override
		public void onAnimationUpdate(ValueAnimator animation) {
			mScrollY = (Float) animation.getAnimatedValue();
			adjustChild();
		}
	};

	SimpleOnGestureListener onGestureListener = new SimpleOnGestureListener() {
		public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
			if (interceptTouchEvent) {
				mScrollY += distanceY / mHeight * 3;
				if (mScrollY < mMinScrollY - 0.2f * num) {
					mScrollY = mMinScrollY - 0.2f * num;
				} else if (mScrollY > mMaxScrollY + 0.2f * num) {
					mScrollY = mMaxScrollY + 0.2f * num;
				}
				adjustChild();
			} else if (horibleScrollEvent && mScrollChild != null) {
				mScrollChild.setX(mScrollChild.getX() - distanceX);
			}
			return true;
		}

		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
			if (interceptTouchEvent) {
				if (mScrollY > mMaxScrollY || mScrollY < mMinScrollY) {
					return false;
				}
				if (Math.abs(velocityY) < 3000) {
					return false;
				}
				// ��һ�����ٶȿ�ʼ���Թ���
				autoScroll(velocityY);
				return true;
			} else if (horibleScrollEvent && mScrollChild != null) {

			}
			return false;
		}

		private void autoScroll(float velocityY) {
			float v = velocityY / mHeight / 1000;// ÿ����mScrollY�ı��ֵ

			long duration = (long) Math.abs(v / ACC * mHeight);

			animator = ValueAnimator.ofFloat(v, 0);
			AnimatorUpdateListener inertiaListener = new MyAnimatorUpdateListener(v, mScrollY);
			animator.addUpdateListener(inertiaListener);
			animator.setInterpolator(new LinearInterpolator());
			animator.setDuration(duration);
			animator.start();
		};
	};

	private ValueAnimator animator;

	class MyAnimatorUpdateListener implements AnimatorUpdateListener {
		// ���ٶ�
		private float v0;
		// ���Ի�����ʼ��y0;
		private float y0;

		private final float a;

		public MyAnimatorUpdateListener(float v0, float y0) {
			this.v0 = v0;
			this.y0 = y0;
			a = v0 > 0 ? -ACC / mHeight : ACC / mHeight;
			// Log.d("px", "a=" + a);
		}

		@Override
		public void onAnimationUpdate(ValueAnimator animation) {
			// ���ݹ�ʽ y= 1/2at*t+v0t
			float v = (Float) animation.getAnimatedValue();
			float t = (v - v0) / a;
			float y = -(v + v0) * 0.5f * t + y0;
			// Log.d("px", "t=" + t + ",y=" + y);
			// Log.d("px", "scroll distance-->"+(v + v0) * 0.5f * t);
			if (y > mMaxScrollY) {
				y = mMaxScrollY;
				animator.cancel();
			} else if (y < mMinScrollY) {
				y = mMinScrollY;
				animator.cancel();
			}
			mScrollY = y;
			adjustChild();
		}
	}

	/**
	 * @����
	 * 
	 * @param i
	 *            λ����Ϣ,�ڼ���item,��Ҳ�������ڵ�2.5��λ��,����С����λ��
	 * @return ����һ��ĳitemӦ�ڰٷ�ֵ��λ��,����ܸ߶ȵİٷֱ�,һ������0%��100%������
	 * @author px / 2016-3-7
	 */
	private float convertVariable(float i) {
		float value;
		if (count >= num) {
			value = (i - mScrollY) / num;// ���ֵ��λ����mScrollY��mScrollY+trueNum�����View,��0�𽥱䵽1
		} else {
			value = ((i + 1) / (count + 1) * num - mScrollY) / num;
		}
		return value;
	}

	/**
	 * @���� ����ٷֱȷֲ�����,����ʹ��y=1-cos(1/2*��x)����,ʹ���漷�ý�,������
	 * 
	 * @param value
	 * @return
	 * @author px / 2016-3-7
	 */
	private float layoutY(float value) {
		if (value > 1) {
			return mHeight;
		} else if (value < 0) {
			return 0;
		}
		float y = (float) (mHeight - Math.cos(Math.PI / 2f * value) * mHeight);

		return y;
	}

	// 删除item的事件监听
	public interface OnDeleteChildListener {
		void onDelete(int position);
	}

	private OnDeleteChildListener onDeleteChildListener;

	public OnDeleteChildListener getOnDeleteChildListener() {
		return onDeleteChildListener;
	}

	public void setOnDeleteChildListener(OnDeleteChildListener onDeleteChildListener) {
		this.onDeleteChildListener = onDeleteChildListener;
	}

}
